{$CLEO .cs}
{$USE CLEO+}
{$USE ini}
nop
script_name 'PYPRICE'
wait 0
wait 0

0@ = 0

if
    0AA2: 31@ = load_dynamic_library "CLEO+.cleo"
jf @ILoveMyGirlfriendJessica
if
    0AA4: 30@ = get_dynamic_library_procedure "GetCleoPlusVersion" library 31@
then
    0AA7: call_function_return 30@ num_params 0 pop 0 29@
end
0AA3: free_dynamic_library 31@
if
    29@ >= 0x1020000
jf @ILoveMyGirlfriendJessica

0@ = 1

if
    0AAB: does_file_exist "cleo\PropertyPriceTweaker.ini"
jf @ILoveMyGirlfriendJessica

0@ = 0

int iBuffer, iPickupIndex, iCounter, iPickupHdle, iIndex, iLastPickup, iCPickupSize, iPoolPtr, iPickupPtr, iType, iMissionsPassed, iGameSaved, iPatchIndex, iPatchApplied, i
float fPriceMult, fPriceMultTemp, fX, fY, fZ, f

read_float_from_ini_file fPriceMult "cleo\PropertyPriceTweaker.ini" section "SETTINGS" key "Multiplier"
clamp_float fPriceMult min 0.0 max 100.0 store_to fPriceMult
if or
    fPriceMult == 0.0
    fPriceMult == 1.0
then
    terminate_this_custom_script
end

get_label_pointer iBuffer = label @Buffer

for i = 0 to 396 step 4
    write_struct_offset iBuffer offset i size 4 value -1
end

// Find pickup pool parameters
read_memory iCPickupSize 0x48AE39 size 1 virtual_protect 0  //  CPickupObjectSize 0x20   
read_memory iPoolPtr 0x48ADC3 size 4 virtual_protect 0  //  pickup pool start 0x9788C0
read_memory iIndex 0x457190 size 4 virtual_protect 0    //  maxObjects 620
iIndex -= 1 //  last index

iLastPickup = iIndex * iCPickupSize
iLastPickup += iPoolPtr

set_script_event_save_confirmation 1 label @CleanUpBeforeSave var_slot iGameSaved


:Main
while true
    wait 1000
    get_int_stat i = integer_stat 147  // Missions Passed    
    if or
        i <> iMissionsPassed
        iGameSaved > -1
    then
        iMissionsPassed = i
        wait 0
        wait 0
        wait 2000
        if
            iGameSaved == -1
        then
            gosub @RevertPickups
        end
        iGameSaved = -1
        wait 0    
        gosub @PatchPickups
    end
    
    i = 400
    
    while i < iPatchIndex
        read_struct_offset fX iBuffer offset i size 4
        i += 4
        read_struct_offset fY iBuffer offset i size 4
        i += 4
        read_struct_offset fZ iBuffer offset i size 4
        i += 4
        if
            locate_camera_distance_to_coordinates fX fY fZ radius 20.0  // If player is near to a property pickup with price > 327600
        then
            if
                iPatchApplied == 0
            jf @Main
            // Property Pickup Money Display Patch for values > 327600 
            write_memory 0x455B42 size 1 value 0x8B virtual_protect 0
            write_memory 0x455B43 size 1 value 0x44 virtual_protect 0
            write_memory 0x455B44 size 1 value 0x24 virtual_protect 0
            write_memory 0x455B45 size 1 value 0x1C virtual_protect 0
            write_memory 0x455B46 size 1 value 0x8B virtual_protect 0
            write_memory 0x455B47 size 1 value 0x40 virtual_protect 0
            write_memory 0x455B48 size 1 value 0x08 virtual_protect 0
            write_memory 0x455B50 size 1 value 0x89 virtual_protect 0
            write_memory 0x455B51 size 1 value 0xC2 virtual_protect 0
            write_memory 0x455B52 size 1 value 0x90 virtual_protect 0
            iPatchApplied = 1
            jump @Main
        end
    end
    
    if
        iPatchApplied == 1
    then
        write_memory 0x455B42 size 1 value 0x0F virtual_protect 0
        write_memory 0x455B43 size 1 value 0xB7 virtual_protect 0
        write_memory 0x455B44 size 1 value 0x86 virtual_protect 0
        write_memory 0x455B45 size 1 value 0x3E virtual_protect 0
        write_memory 0x455B46 size 1 value 0x01 virtual_protect 0
        write_memory 0x455B47 size 1 value 0x00 virtual_protect 0
        write_memory 0x455B48 size 1 value 0x00 virtual_protect 0
        write_memory 0x455B50 size 1 value 0x8D virtual_protect 0
        write_memory 0x455B51 size 1 value 0x14 virtual_protect 0
        write_memory 0x455B52 size 1 value 0x80 virtual_protect 0
        iPatchApplied = 0
    end               
end    

{
// CPickup Structure
00000000 m_fRevenueValue dd ?
00000004 m_pObject       dd ?         
00000008 m_dwAmmo        dd ?
0000000C m_dwRegenerationTime dd ?
00000010 m_vPos CCompressedVector ?
00000016 m_nMoneyPerDay  dw ?         
00000018 m_wModelId      dw ?
0000001A m_wReferenceIndex dw ?       
0000001C m_PickupType    db ?
0000001D m_nFlags        db ?
0000001E _pad            db 2 dup(?)
00000020 CPickup         ends
}

:PatchPickups
fPriceMultTemp = fPriceMult
iCounter = 0
iPickupIndex = 0
iPatchIndex = 400

for iPickupPtr = iPoolPtr to iLastPickup step iCPickupSize
    read_struct_offset iType iPickupPtr offset 0x1C size 1
    if
        iType == 18 // PropertyForSale
    then
        if
            iPickupIndex < 400
        return_if_false
        i = iPickupPtr - iPoolPtr
        iIndex = i / iCPickupSize 
        read_struct_offset i iPickupPtr offset 0x1A size 2
        i *= 0x10000    // shift reference index to the upper word
        iPickupHdle = i + iIndex   // add the index to create a handle
        write_struct_offset iBuffer offset iPickupIndex size 4 value iPickupHdle  // Write handle into buffer for reverting it later
        iPickupIndex += 4
        gosub @SetPrice
        if and
            i > 327600  // Max number that Buy Property Pickups can display. If the price exceeds this number, we need to apply a patch.
            iPatchIndex < 640
        then
            get_pickup_coordinates iPickupHdle position_to fX fY fZ
            write_struct_offset iBuffer offset iPatchIndex size 4 value fX
            iPatchIndex += 4
            write_struct_offset iBuffer offset iPatchIndex size 4 value fY
            iPatchIndex += 4
            write_struct_offset iBuffer offset iPatchIndex size 4 value fZ
            iPatchIndex += 4
        end                        
    end
    iCounter += 1
    if
        iCounter > 100
    then
        wait 0  // Reduce overheat
        iCounter = 0
    end
end
return


:RevertPickups
fPriceMultTemp = 1.0
fPriceMultTemp /= fPriceMult

for iPickupIndex = 0 to 396 step 4
    read_struct_offset iPickupHdle iBuffer offset iPickupIndex size 4
    if
        iPickupHdle > -1
    return_if_false
    if
        does_pickup_exist iPickupHdle
    then
        get_pickup_pointer iPickupHdle store_to iPickupPtr
        read_struct_offset iType iPickupPtr offset 0x1C size 1
        if
            iType == 18 // PropertyForSale
        then
            gosub @SetPrice
        end
    end
    write_struct_offset iBuffer offset iPickupIndex size 4 value -1
end
return


:SetPrice
read_struct_offset i iPickupPtr offset 0x08 size 4
cset_lvar_float_to_lvar_int f = i 
f *= fPriceMultTemp
f += 0.2    // Prevent float precision problem
cset_lvar_int_to_lvar_float i = f
write_struct_offset iPickupPtr offset 0x08 size 4 value i
return


:CleanUpBeforeSave
gosub @RevertPickups
return_script_event


:Buffer
hex
    00(640)
end


:ILoveMyGirlfriendJessica
TIMERA = 0

repeat
    if
        0@ == 0
    then
        0AD1: print_formatted_now "Property Price Tweaker: CLEO+ 1.2.0 or newer required. Please download and install it in cleo folder." time 100
    else
        0AD1: print_formatted_now "Property Price Tweaker: Failed to find the .ini file. Check the file path or file name." time 100
    end
    wait 0
    if or
        89E7: not is_player_control_on $PLAYER_CHAR
        016B: fading
    then
        TIMERA = 0
    end
until TIMERA > 8000         

terminate_this_custom_script